#include <WS2tcpip.h>
#include "BeginConnDestHandlerBase.h"
#include "GlobalContext.h"

using namespace log4cplus;

Logger BeginConnDestHandlerBase::log=GetLogger(LOG4CPLUS_TEXT("BeginConnDestHandlerBase"));

ITransHandler::ExecuteResultCode BeginConnDestHandlerBase::Execute(ClientContext &client,OVERLAPPED *ovl)
{
	unsigned int clienSequenceNo=client.SequenceNo();
	LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]Begin to connect destination server."),clienSequenceNo);
	SOCKET sock=::WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sock==INVALID_SOCKET)
	{
		int errorCode=::WSAGetLastError();
		LOG4CPLUS_ERROR_FMT(log,LOG4CPLUS_TEXT("[Client%d]Create destination server socket failed,error code:%d."),clienSequenceNo,errorCode);
		return this->OnExecuteError(client,ovl,ITransHandler::ExecuteFatalError);
	}
	this->CreateSocketPostProcess(client,ovl,sock);
	if(!GlobalContext::Instance().AssociateHandleWithIOCP(reinterpret_cast<HANDLE>(sock),client.SequenceNo()))
	{
		LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]Associate destination server socket successed."),clienSequenceNo);
		return this->OnExecuteError(client,ovl,ITransHandler::ExecuteFatalError);
	}
	LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]Create and associate  destination server socket successed."),clienSequenceNo);

	addrinfo *result=NULL;
	addrinfo hint;
	::ZeroMemory(&hint,sizeof(addrinfo));
	hint.ai_family=AF_INET;
	hint.ai_socktype=SOCK_STREAM;
	hint.ai_protocol=IPPROTO_TCP;
	int resolveResult=::getaddrinfo(this->DestinationServerName(client).c_str(),NULL,&hint,&result);
	if(resolveResult!=0||result==NULL)
	{
		LOG4CPLUS_ERROR_FMT(log,LOG4CPLUS_TEXT("[Client%d]Resolve destination address failed,error code:%d."),clienSequenceNo,resolveResult);
		return this->OnExecuteError(client,ovl,ITransHandler::ExecuteFatalError);
	}
	LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]Resolve original server ip address successed."),clienSequenceNo);

	ITransHandler *self = client.SetCurrentHandler(this->GetNextHandler());
	sockaddr_in server;
	server.sin_family = AF_INET;
    server.sin_addr.s_addr = reinterpret_cast<sockaddr_in*>(result->ai_addr)->sin_addr.s_addr;
    server.sin_port = htons(DestinationServerPort(client));
	if(::WSAConnect(sock,reinterpret_cast<sockaddr*>(&server),sizeof(sockaddr_in),NULL,NULL,NULL,NULL)==SOCKET_ERROR)
	{
		int errorCode=::WSAGetLastError();
		LOG4CPLUS_ERROR_FMT(log,LOG4CPLUS_TEXT("[Client%d]Connect destination server failed,error code:%d."),clienSequenceNo,errorCode);
		::freeaddrinfo(result);
		ExecuteResultCode errorResultCode = this->OnExecuteError(client,ovl,ITransHandler::ExecuteFatalError);
		delete self;
		return errorResultCode;
	}
	LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]Destination server connected."),clienSequenceNo);
	::freeaddrinfo(result);
	ITransHandler::ExecuteResultCode resultCode = OnConnected(client,ovl);
	delete self;
	LOG4CPLUS_DEBUG_FMT(log,LOG4CPLUS_TEXT("[Client%d]End to connect destination server."),clienSequenceNo);
	return resultCode;
}

ITransHandler::ExecuteResultCode BeginConnDestHandlerBase::OnConnected(ClientContext &client,OVERLAPPED *ovl)
{
	::PostQueuedCompletionStatus(GlobalContext::Instance().IOCP(),0,static_cast<ULONG_PTR>(client.SequenceNo()),client.OVL());
	return ITransHandler::ExecuteOK;
}

const std::string& BeginConnDestHandlerBase::DestinationServerName(ClientContext &client)
{
	return client.HttpRequest().ServerName();
}

const unsigned short BeginConnDestHandlerBase::DestinationServerPort(ClientContext &client)
{
	return client.HttpRequest().ServerPort();
}